/**
 * Copyright 2015 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2015年4月8日 下午4:57:44
 */
package com.absir.appserv.system.service;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.absir.appserv.init.InitBeanFactory;
import com.absir.appserv.system.adapter.SocketAdapter;
import com.absir.appserv.system.adapter.SocketAdapter.CallbackAdapte;
import com.absir.appserv.system.adapter.SocketAdapter.CallbackTimeout;
import com.absir.appserv.system.converter.BodyMsgPack.CallbackMessagePack;
import com.absir.appserv.system.helper.HelperDatabind;
import com.absir.appserv.system.helper.HelperEncrypt;
import com.absir.appserv.system.slave.ISlaveCallback;
import com.absir.appserv.system.slave.SlaveChannelResolver;
import com.absir.appserv.system.slave.SlaveChannelResolverImpl;
import com.absir.bean.basis.Base;
import com.absir.bean.core.BeanFactoryUtils;
import com.absir.bean.inject.value.Bean;
import com.absir.bean.inject.value.Inject;
import com.absir.bean.inject.value.InjectType;
import com.absir.bean.inject.value.Orders;
import com.absir.bean.inject.value.Stopping;
import com.absir.bean.inject.value.Value;
import com.absir.context.core.ContextUtils;
import com.absir.context.schedule.value.Schedule;
import com.absir.core.base.Environment;
import com.absir.core.kernel.KernelLang.ObjectEntry;

/**
 * @author absir
 *
 */
@Base
@Bean
public class SlaveService {

	/** LOGGER */
	protected static final Logger LOGGER = LoggerFactory.getLogger(SlaveService.class);

	/** ME */
	public static final SlaveService ME = BeanFactoryUtils.get(SlaveService.class);

	/** ip */
	@Value("server.slave.ip")
	protected String ip;

	/** port */
	@Value("server.slave.port")
	protected int port = 28890;

	/** port */
	@Value("server.slave.group")
	protected String group = "0";

	/** key */
	@Value("server.slave.key")
	protected String key = "absir@qq.com";

	/** url */
	@Value("server.slave.url")
	protected String url;

	/** slaveKey */
	protected String slaveKey;

	/** beat */
	@Value("server.slave.beat")
	protected byte[] beat = "b".getBytes();

	/** socketAdapter */
	protected SocketAdapter socketAdapter;

	/** slaveCallbacks */
	@Inject(type = InjectType.Selectable)
	@Orders
	protected ISlaveCallback[] slaveCallbacks;

	/** channelResolver */
	protected SlaveChannelResolver channelResolver;

	/**
	 * @return the channelResolver
	 */
	public SlaveChannelResolver getChannelResolver() {
		return channelResolver;
	}

	/**
	 * 初始化服务
	 */
	@Inject
	public void injectService() {
		socketAdapter = createAdapter();
		socketAdapter.setBeats(beat);
		socketAdapter.setCallbackConnect(new CallbackAdapte() {

			@Override
			public void doWith(SocketAdapter adapter, byte[] buffer) {
				connectAdapter(adapter);
			}
		});
		socketAdapter.setCallbackDisconnect(new CallbackAdapte() {

			@Override
			public void doWith(SocketAdapter adapter, byte[] buffer) {
				disconnectAdapter(adapter);
			}
		});
		socketAdapter.setAcceptCallback(new CallbackAdapte() {

			@Override
			public void doWith(SocketAdapter adapter, byte[] buffer) {
				acceptAdapter(adapter, buffer);
			}
		});
		socketAdapter.setRegisterCallback(new CallbackAdapte() {

			@Override
			public void doWith(SocketAdapter adapter, byte[] buffer) {
				registerAdapter(adapter, buffer);
			}
		});
		socketAdapter.setReceiveCallback(new CallbackAdapte() {

			@Override
			public void doWith(SocketAdapter adapter, byte[] buffer) {
				receiveCallback(adapter, buffer);
			}
		});

		if (slaveCallbacks != null) {
			Map<Integer, ObjectEntry<CallbackAdapte, CallbackTimeout>> receiveCallbacks = socketAdapter.getReceiveCallbacks();
			for (ISlaveCallback slaveCallback : slaveCallbacks) {
				int callbackIndex = slaveCallback.getCallbackIndex();
				if (callbackIndex <= socketAdapter.getMinCallbackIndex() && !receiveCallbacks.containsKey(callbackIndex)) {
					socketAdapter.putReceiveCallbacks(callbackIndex, slaveCallback);
				}
			}
		}

		if (channelResolver == null) {
			channelResolver = new SlaveChannelResolverImpl();
		}

		slaveKey = key;
		// 接受密钥
		socketAdapter.putReceiveCallbacks(2, new CallbackAdapte() {

			@Override
			public void doWith(SocketAdapter adapter, byte[] buffer) {
				slaveKey = new String(buffer, 5, buffer.length - 5);
			}
		});
	}

	/**
	 * 连接服务器
	 */
	@Schedule(fixedDelay = 60000, initialDelay = 15000)
	public void connectService() {
		if (Environment.isActive()) {
			socketAdapter.connect();
		}
	}

	/**
	 * 关闭服务
	 */
	@Stopping
	public void closeService() {
		socketAdapter.disconnect(null);
	}

	/**
	 * @return the key
	 */
	public String getKey() {
		return key;
	}

	/**
	 * @return the url
	 */
	public String getUrl() {
		return url;
	}

	/**
	 * @return the slaveKey
	 */
	public String getSlaveKey() {
		return slaveKey;
	}

	/**
	 * @return the socketAdapter
	 */
	public SocketAdapter getSocketAdapter() {
		return socketAdapter;
	}

	/**
	 * 创建
	 * 
	 * @return
	 */
	protected SocketAdapter createAdapter() {
		return new SocketAdapter();
	}

	/**
	 * 连接
	 * 
	 * @param adapter
	 */
	protected void connectAdapter(SocketAdapter adapter) {
		if (ip != null) {
			try {
				SocketChannel socketChannel = SocketChannel.open();
				socketChannel.configureBlocking(true);
				adapter.setSocket(socketChannel.socket());
				socketChannel.connect(new InetSocketAddress(ip, port));
				adapter.receiveSocketChannelStart();

			} catch (Exception e) {
				LOGGER.error("connectAdapter error", e);
			}
		}
	}

	/**
	 * 断开重连
	 * 
	 * @param adapter
	 */
	protected void disconnectAdapter(SocketAdapter adapter) {
		adapter.connect();
	}

	/**
	 * 接收
	 * 
	 * @param adapter
	 * @param buffer
	 */
	protected void acceptAdapter(SocketAdapter adapter, byte[] buffer) {
		String registerKey = HelperEncrypt.encryptionMD5(key, buffer) + "," + group + "," + InitBeanFactory.ME.getVersion();
		byte[] registerData = adapter.sendDataBytes(registerKey.getBytes(), false, false, 0, null);
		adapter.sendData(registerData);
	}

	/**
	 * 注册
	 * 
	 * @param adapter
	 * @param buffer
	 */
	protected void registerAdapter(SocketAdapter adapter, byte[] buffer) {
		if (Arrays.equals(buffer, SocketAdapter.ok)) {
			adapter.setRegistered(true);

		} else {
			adapter.close();
			LOGGER.error("registerAdapter failed status : " + new String(buffer));
		}
	}

	/**
	 * 接收数据
	 * 
	 * @param adapter
	 * @param buffer
	 */
	protected void receiveCallback(SocketAdapter adapter, byte[] buffer) {
		// LOGGER.info("receiveCallback" + buffer);
	}

	/**
	 * @param uri
	 * @param postData
	 * @param callbackMessagePack
	 * @throws IOException
	 */
	public void sendData(String uri, Object postData, CallbackMessagePack<?> callbackMessagePack) throws IOException {
		sendData(uri, postData, 30000, callbackMessagePack);
	}

	/**
	 * @param uri
	 * @param postData
	 * @param timeout
	 * @param callbackMessagePack
	 * @throws IOException
	 */
	public void sendData(String uri, Object postData, int timeout, CallbackMessagePack<?> callbackMessagePack) throws IOException {
		socketAdapter.sendData(uri.getBytes(ContextUtils.getCharset()), true, false,
				postData == null ? null : HelperDatabind.writeAsBytes(postData), timeout, callbackMessagePack);
	}

}
